#ifndef QUATERNION_H
#define QUATERNION_H

#include "bga/math/Vector3.h"
#include <string>

/**
 * \namespace bga
 * \brief API namespace
 */
namespace bga
{

class BOARD_HELL_ENGINE Quaternion : public Tuple<4, double>
{
	public:
        /**
         * \brief Constructor with four parameters
         * \param s : s parameter
         * \param x : x parameter
         * \param y : y parameter
         * \param z : z parameter
         * \param mode : true if we use the quaternion for a rotation
         */
		Quaternion(double s = 1, double x = 0, double y = 0, double z = 0, bool mode = false);

		/**
         * \brief Constructor with four parameters
         * \param s : s parameter
         * \param v : vector of x,y and z parameters
         * \param mode : true if we use the quaternion for a rotation
         */
		Quaternion(double s, const Vector3d& v, bool mode = false);

		/**
		 * Copy constructor
		 * \param q : Quaternion to copy
		 */
		Quaternion(const Quaternion& q);

		/**
		 * Destructor
		 */
		virtual ~Quaternion();

        /**
         * \brief Compute the norm of the quaternion
         * \return value of the norm
         */
		double getNorm() const;

		/**
		 * \brief Multiply the current Quaternion with an other quaternion
		 * \param q : Quaternion used to multiply the current Quaternion
		 * \return Resulting Quaternion
		 */
		Quaternion operator*(const Quaternion& q) const;

		/**
		 * \brief Multiply the current Quaternion with a vector3
		 * \param v : Vector3 used to multiply the current Quaternion
		 * \return Resulting Quaternion
		 */
		Quaternion operator*(const Vector3d& v) const;

		/**
		 * \brief Multiply the current Quaternion with a value
		 * \param value : value used to multiply the current Quaternion
		 * \return Resulting Quaternion
		 */
		Quaternion operator*(const double value) const;

		/**
		 * \brief Add the current Quaternion with an other quaternion
		 * \param q : Quaternion to add to the current Quaternion
		 * \return Resulting Quaternion
		 */
		Quaternion operator+(const Quaternion& q) const;

		/**
		 * \brief Add the current Quaternion with a vector3
		 * \param v : Vector3 to add to the current Quaternion
		 * \return Resulting Quaternion
		 */
		Quaternion operator+(const Vector3d& v) const;

		/**
		 * \brief Add the current Quaternion with an other quaternion and store result
		 * \param q : Quaternion to add to the current Quaternion
		 * \return Resulting Quaternion
		 */
		Quaternion& operator+=(const Quaternion& q);

		/**
		 * \brief Sub the current Quaternion with a Vector3 and store result
		 * \param q : Quaternion to Sub to the current Quaternion
		 * \return Resulting Quaternion
		 */
		Quaternion& operator-=(const Quaternion& q);

		/**
		 * \brief Add the current Quaternion with a Vector3 and store result
		 * \param v : Vector3 to add to the current Quaternion
		 * \return Resulting Quaternion
		 */
		Quaternion& operator+=(const Vector3d& v);

		/**
		 * \brief Sub the current Quaternion with a Vector3 and store result
		 * \param v : Vector3 to sub to the current Quaternion
		 * \return Resulting Quaternion
		 */
		Quaternion& operator-=(const Vector3d& v);

		/**
		 * \brief Assign a Quaternion with another Quaternion
		 * \param q : Quaternion to assign to the current Quaternion
		 * \return Resulting Quaternion
		 */
		Quaternion& operator=(const Quaternion& q);

		/**
		 * \brief Test the current Quaternion is equal to another
		 * \param q : Quaternion to test the equality with the current Quaternion
		 * \return true if the both quaternions are equal, false else
		 */
		bool operator==(const Quaternion& q) const;

		/**
		 * \brief Test the current Quaternion is not equal to another
		 * \param q : Quaternion to test the non equality with the current Quaternion
		 * \return true if the both quaternions are not equal, false else
		 */
		bool operator!=(const Quaternion& q) const;

		/**
		 * \brief compute and return the conjugation of the current quaternion
		 * \return The conjuguate quaternion
		 */
		Quaternion getConjugation() const;

		/**
		 * \brief compute and return the reciprocal of the current quaternion
		 * \return The inverse quaternion
		 */
		Quaternion getReciprocal() const;

		/**
		 * \brief Return the Vector3 of the x, y and z attributes of the current quaternion
		 * \return The Vector3 of the x, y and z attributes
		 */
		Vector3d toVector3D() const;

		/**
		 * \brief Compute a dot product between the current Quaternion and an other
		 * \return The value of the dot product
		 */
		double dot(const Quaternion& q) const;

		/**
		 * \brief Normalize the current Quaternion
		 */
		void normalize();

		/**
		 * \brief Compute the matrix matching with Quaternion in parameter
		 * \return The matrix of the Quaternion
		 */
		float * quaternionToMatrix(const Quaternion& q);

		/**
		 * \brief Return a textual representation of the Quaternion
		 * \return The textual representation of the quaternion
		 */
		std::string toString() const;

		/**
		 * \brief Get the s element of the quaternion
		 * \return The s element
		 */
		double getS() const;

		/**
		 * \brief Get the x element of the quaternion
		 * \return The x element
		 */
		double getX() const;

		/**
		 * \brief Get the y element of the quaternion
		 * \return The y element
		 */
		double getY() const;

		/**
		 * \brief Get the z element of the quaternion
		 * \return The z element
		 */
		double getZ() const;

		/**
		 * \brief Set the s element of the quaternion
		 * \param s : the new s element
		 */
		void setS(double s);

		/**
		 * \brief Set the x element of the quaternion
		 * \param x : the new x element
		 */
		void setX(double x);

		/**
		 * \brief Set the y element of the quaternion
		 * \param y : the new y element
		 */
		void setY(double y);

		/**
		 * \brief Set the z element of the quaternion
		 * \param z : the new z element
		 */
		void setZ(double z);

		/**
		 * \brief Compute a slerp interpolation between the current quaternion and another
		 * \param q : The final quaternion of the interpolation
		 * \param t : value of the interpolation between 0 and 1
		 */
		Quaternion slerp(const Quaternion& q, float t) const;

	protected:

	public:
        double& s;
        double& x;
        double& y;
        double& z;


};

}

#endif // QUATERNION_H

